import * as Cesium from 'cesium'

/**
 * @param viewer  {object} 三维对象
 * @param options {object} 初始化参数
 * @constructor
 */
export class MeasureTools {
  /**
   * 绘制对象
   * @param viewer
   * @param options
   * @constructor
   */
  constructor(viewer, options = {}) {
    this.viewer = viewer
    this.options = options
    this.drawLayer = null
    this.handler = null
    this.basePath = ''
    this.init()
  };

  /**
   * 初始化画板
   */
  init() {
    if (this.viewer && this.viewer instanceof Cesium.Viewer) {
      if (this.handler && !this.handler.isDestroyed()) {
        this.handler.destroy()
      }
      let findDataSource = this.viewer.dataSources.getByName('measureLayer')
      if (findDataSource.length > 0) {
        this.drawLayer = findDataSource[0]
      } else {
        this.drawLayer = new Cesium.CustomDataSource('measureLayer')
        this.viewer.dataSources.add(this.drawLayer)
      }
      this.basePath = this.options.basePath || ''
    }
  };

  /**
   * 重置点击事件
   */
  removeDrawMeasureAction() {
    if (this.handler && !this.handler.isDestroyed()) {
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
    }
  }

  /***
   * 坐标转换 84转笛卡尔
   * @param {Object} {lng,lat,alt} 地理坐标
   * @return {Object} Cartesian3 三维位置坐标
   */
  transformWGS84ToCartesian(position, alt) {
    if (this.viewer) {
      const iCartesian3 = Cesium.Cartesian3.fromDegrees(
        position.lng || position.lon,
        position.lat,
        position.alt = alt || position.alt,
        Cesium.Ellipsoid.WGS84
      )
      return position ? iCartesian3 : Cesium.Cartesian3.ZERO
    }
  };

  /***
   * 坐标数组转换 笛卡尔转84
   * @param {Array} WSG84Arr {lng,lat,alt} 地理坐标数组
   * @param {Number} alt 拔高
   * @return {Array} Cartesian3 三维位置坐标数组
   */
  transformWGS84ArrayToCartesianArray(WSG84Arr, alt) {
    if (this.viewer && WSG84Arr) {
      const _this = this
      return WSG84Arr
        ? WSG84Arr.map(item => {
          return _this.transformWGS84ToCartesian(item, alt)
        })
        : []
    }
  };

  /***
   * 坐标转换 笛卡尔转84
   * @param {Object} Cartesian3 三维位置坐标
   * @return {Object} {lng,lat,alt} 地理坐标
   */
  transformCartesianToWGS84(cartesian) {
    if (this.viewer && cartesian) {
      const ellipsoid = Cesium.Ellipsoid.WGS84
      const cartographic = ellipsoid.cartesianToCartographic(cartesian)
      return {
        lng: Cesium.Math.toDegrees(cartographic.longitude),
        lat: Cesium.Math.toDegrees(cartographic.latitude),
        alt: cartographic.height
      }
    }
  };

  /***
   * 坐标数组转换 笛卡尔转86
   * @param {Array} cartesianArr 三维位置坐标数组
   * @return {Array} {lng,lat,alt} 地理坐标数组
   */
  transformCartesianArrayToWGS84Array(cartesianArr) {
    if (this.viewer) {
      const _this = this
      return cartesianArr
        ? cartesianArr.map(item => {
          return _this.transformCartesianToWGS84(item)
        })
        : []
    }
  };

  /**
   * 84坐标转弧度坐标
   * @param {Object} position wgs84
   * @return {Object} Cartographic 弧度坐标
   */
  transformWGS84ToCartographic(position) {
    return position
      ? Cesium.Cartographic.fromDegrees(
        position.lng || position.lon,
        position.lat,
        position.alt
      )
      : Cesium.Cartographic.ZERO
  };

  /**
   * 拾取位置点
   * @param {Object} px 屏幕坐标
   * @return {Object} Cartesian3 三维坐标
   */
  getCatesian3FromPX(px) {
    if (this.viewer && px) {
      const picks = this.viewer.scene.drillPick(px)
      let cartesian = null
      let isOn3dtiles = false
      let isOnTerrain = false
      // drillPick
      for (let i in picks) {
        let pick = picks[i]
        if ((pick && pick.primitive instanceof Cesium.Cesium3DTileFeature) ||
          (pick && pick.primitive instanceof Cesium.Cesium3DTileset) ||
          (pick && pick.primitive instanceof Cesium.Model)) { //模型上拾取
          isOn3dtiles = true
        }
        // 3dtilset
        if (isOn3dtiles) {
          this.viewer.scene.pick(px) // pick
          cartesian = this.viewer.scene.pickPosition(px)
          if (cartesian) {
            let cartographic = Cesium.Cartographic.fromCartesian(cartesian)
            if (cartographic.height < 0) cartographic.height = 0
            let lon = Cesium.Math.toDegrees(cartographic.longitude)
            let lat = Cesium.Math.toDegrees(cartographic.latitude)
            let height = cartographic.height
            cartesian = this.transformWGS84ToCartesian({
              lng: lon,
              lat: lat,
              alt: height
            })
          }
        }
      }
      // 地形
      let boolTerrain = this.viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider
      // Terrain
      if (!isOn3dtiles && !boolTerrain) {
        const ray = this.viewer.scene.camera.getPickRay(px)
        if (!ray) return null
        cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene)
        isOnTerrain = true
      }
      // 地球
      if (!isOn3dtiles && !isOnTerrain && boolTerrain) {
        cartesian = this.viewer.scene.camera.pickEllipsoid(px, this.viewer.scene.globe.ellipsoid)
      }
      if (cartesian) {
        let position = this.transformCartesianToWGS84(cartesian)
        if (position.alt < 0) {
          cartesian = this.transformWGS84ToCartesian(position, 0.1)
        }
        return cartesian
      }
      return false
    }
  };

  /**
   * 获取84坐标的距离
   * @param {*} positions
   */
  getPositionDistance(positions) {
    let distance = 0
    for (let i = 0; i < positions.length - 1; i++) {
      let point1cartographic = this.transformWGS84ToCartographic(positions[i])
      let point2cartographic = this.transformWGS84ToCartographic(positions[i + 1])
      let geodesic = new Cesium.EllipsoidGeodesic()
      geodesic.setEndPoints(point1cartographic, point2cartographic)
      let s = geodesic.surfaceDistance
      s = Math.sqrt(
        Math.pow(s, 2) +
        Math.pow(point2cartographic.height - point1cartographic.height, 2)
      )
      distance = distance + s
    }
    return distance.toFixed(3)
  };

  /**
   * 计算一组坐标组成多边形的面积
   * @param {*} positions
   */
  getPositionsArea(positions) {
    let result = 0
    if (positions) {
      let h = 0
      let ellipsoid = Cesium.Ellipsoid.WGS84
      positions.push(positions[0])
      for (let i = 1; i < positions.length; i++) {
        let oel = ellipsoid.cartographicToCartesian(
          this.transformWGS84ToCartographic(positions[i - 1])
        )
        let el = ellipsoid.cartographicToCartesian(
          this.transformWGS84ToCartographic(positions[i])
        )
        h += oel.x * el.y - el.x * oel.y
      }
      result = Math.abs(h).toFixed(2)
    }
    return result
  }

  /**
   * 测距
   * @param {*} options
   */
  drawLineMeasureGraphics(options = {}) {
    this.removeDrawMeasureAction()
    if (this.viewer && options) {
      let positions = []
      let lineEntity = new Cesium.Entity()
      const _this = this
      let lineObj = null
      this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
      // left
      this.handler.setInputAction(function (movement) {
        let cartesian = _this.getCatesian3FromPX(movement.position)
        if (cartesian && cartesian.x) {
          if (positions.length === 0) {
            positions.push(cartesian.clone())
          }
          // 添加量测信息点
          addInfoPoint(cartesian)
          positions.push(cartesian)
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
      this.handler.setInputAction(function (movement) {
        let cartesian = _this.getCatesian3FromPX(movement.endPosition)
        if (positions.length >= 2) {
          if (cartesian && cartesian.x) {
            positions.pop()
            positions.push(cartesian)
          }
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      // right
      this.handler.setInputAction(function (movement) {
        _this.handler.destroy()
        let cartesian = _this.getCatesian3FromPX(movement.position)
        addInfoPoint(cartesian)
        if (typeof options.callback === 'function') {
          options.callback(_this.transformCartesianArrayToWGS84Array(positions), lineObj)
        }
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
      lineEntity.polyline = {
        width: options.width || 3,
        material: options.material || Cesium.Color.fromCssColorString('#4DFFFF'), //Cesium.Color.BLUE.withAlpha(0.8)
        clampToGround: options.clampToGround || false
      }
      lineEntity.polyline.positions = new Cesium.CallbackProperty(function () {
        return positions
      }, false)
      lineObj = this.drawLayer.entities.add(lineEntity)

      //添加坐标点
      const addInfoPoint = (position) => {
        let labelEntity = new Cesium.Entity()
        labelEntity.position = position
        labelEntity.point = {
          pixelSize: 5,
          outlineColor: Cesium.Color.RED,
          outlineWidth: 2
        }
        const distance = _this.getPositionDistance(_this.transformCartesianArrayToWGS84Array(positions))
        labelEntity.label = {
          text: distance <= 0 ? '起点' : distance >= 1000 ? (distance / 1000).toFixed(2) + '公里' : (distance / 1).toFixed(2) + '米', //长度单位选择
          show: true,
          showBackground: true,
          font: '16px monospace',
          horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          pixelOffset: new Cesium.Cartesian2(-20, -80) //left top
        }
        _this.drawLayer.entities.add(labelEntity)
      }
    }
  };

  /**
   * 测面积
   * @param {*} options
   */
  drawAreaMeasureGraphics(options = {}) {
    this.removeDrawMeasureAction()
    if (this.viewer && options) {
      let positions = []
      let polygon = new Cesium.PolygonHierarchy()
      let polygonEntity = new Cesium.Entity()
      const _this = this
      let polyObj = null
      let label = ''
      this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
      // left
      this.handler.setInputAction(function (movement) {
        const cartesian = _this.getCatesian3FromPX(movement.position)
        if (cartesian && cartesian.x) {
          if (positions.length === 0) {
            polygon.positions.push(cartesian.clone())
            positions.push(cartesian.clone())
          }
          positions.push(cartesian.clone())
          // 添加信息点
          addInfoPoint(cartesian, false)
          polygon.positions.push(cartesian.clone())
          if (!polyObj) create()
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
      // mouse
      this.handler.setInputAction(function (movement) {
        const cartesian = _this.getCatesian3FromPX(movement.endPosition)
        // var cartesian = $this._viewer.scene.camera.pickEllipsoid(movement.endPosition, $this._viewer.scene.globe.ellipsoid);
        if (positions.length >= 2) {
          if (cartesian && cartesian.x) {
            positions.pop()
            positions.push(cartesian)
            polygon.positions.pop()
            polygon.positions.push(cartesian)
          }
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

      // right
      this.handler.setInputAction(function (movement) {
        let cartesian = _this.getCatesian3FromPX(movement.position)
        _this.handler.destroy()
        positions.push(positions[0])
        addInfoPoint(positions[0], false)
        const centerCartesian = computeCentroidOfPolygon(positions)
        const centerPosition = _this.viewer.scene.globe.ellipsoid.cartographicToCartesian(centerCartesian) //计算多边形重心
        // 添加信息点
        addInfoPoint(centerPosition, true)
        if (typeof options.callback === 'function') {
          options.callback(_this.transformCartesianArrayToWGS84Array(positions), polyObj)
        }
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
      const create = () => {
        polygonEntity.polyline = {
          width: 3,
          material: Cesium.Color.fromCssColorString('#4DFFFF'), //Cesium.Color.BLUE.withAlpha(0.8)
          clampToGround: options.clampToGround || false
        }
        polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
          return positions
        }, false)
        polygonEntity.polygon = {
          hierarchy: new Cesium.CallbackProperty(function () {
            return polygon
          }, false),
          material: Cesium.Color.LIGHTGREY.withAlpha(0.3),
          clampToGround: options.clampToGround || false
        }
        polyObj = _this.drawLayer.entities.add(polygonEntity)
      }
      //添加点
      const addInfoPoint = (position, isShow) => {
        let labelEntity = new Cesium.Entity()
        labelEntity.position = position
        if (!isShow) {
          labelEntity.point = {
            pixelSize: 5,
            outlineColor: Cesium.Color.RED,
            outlineWidth: 2
          }
        }
        const area = _this.getPositionsArea(_this.transformCartesianArrayToWGS84Array(positions))
        if (isShow) {
          labelEntity.label = {
            text: area < 1000000 ? area + '平方米' : (area / 1000000.0).toFixed(2) + '平方公里',
            show: true,
            showBackground: true,
            font: '16px monospace',
            horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(-40, -80) //left top
          }
        }
        _this.drawLayer.entities.add(labelEntity)
      }
      /**
       * 计算多边形的重心点
       * @param {*} positions
       */
      const computeCentroidOfPolygon = (positions) => {
        let x = []
        let y = []
        for (let i = 0; i < positions.length; i++) {
          let cartographic = Cesium.Cartographic.fromCartesian(positions[i])
          x.push(cartographic.longitude)
          y.push(cartographic.latitude)
        }
        let x0 = 0.0
        let y0 = 0.0
        let x1 = 0.0
        let y1 = 0.0
        let signedArea = 0.0
        let a = 0.0
        let centroidx = 0.0
        let centroidy = 0.0
        for (let i = 0; i < positions.length; i++) {
          x0 = x[i]
          y0 = y[i]
          if (i === positions.length - 1) {
            x1 = x[0]
            y1 = y[0]
          } else {
            x1 = x[i + 1]
            y1 = y[i + 1]
          }
          a = x0 * y1 - x1 * y0
          signedArea += a
          centroidx += (x0 + x1) * a
          centroidy += (y0 + y1) * a
        }
        signedArea *= 0.5
        centroidx /= (6.0 * signedArea)
        centroidy /= (6.0 * signedArea)
        return new Cesium.Cartographic(centroidx, centroidy, 0)
      }
    }
  };

  /**
   * 画三角量测
   * @param {*} options
   */
  drawTrianglesMeasureGraphics(options = {}) {
    this.removeDrawMeasureAction()
    options.style = options.style || {
      width: 3,
      material: Cesium.Color.fromCssColorString('#4DFFFF'), //Cesium.Color.BLUE.withAlpha(0.8)
    }
    if (this.viewer && options) {
      let trianglesEntity = new Cesium.Entity()
      let tempLineEntity = new Cesium.Entity()
      let tempLineEntity2 = new Cesium.Entity()
      let positions = []
      let tempPoints = []
      let tempPoints2 = []
      const _this = this

      // 高度
      const getHeading = (startPosition, endPosition) => {
        if (!startPosition && !endPosition) return 0
        if (Cesium.Cartesian3.equals(startPosition, endPosition)) return 0
        let cartographic = Cesium.Cartographic.fromCartesian(startPosition)
        let cartographic2 = Cesium.Cartographic.fromCartesian(endPosition)
        return (cartographic2.height - cartographic.height).toFixed(2)
      }

      // 偏移点
      const computesHorizontalLine = (positions) => {
        let cartographic = Cesium.Cartographic.fromCartesian(positions[0])
        let cartographic2 = Cesium.Cartographic.fromCartesian(positions[1])
        return Cesium.Cartesian3.fromDegrees(
          Cesium.Math.toDegrees(cartographic.longitude),
          Cesium.Math.toDegrees(cartographic.latitude),
          cartographic2.height
        )
      }
      this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
      // left
      this.handler.setInputAction(function (movement) {
        const position = _this.getCatesian3FromPX(movement.position)
        if (!position && !position.z) return false
        if (positions.length === 0) {
          positions.push(position.clone())
          positions.push(position.clone())
          tempPoints.push(position.clone())
          tempPoints.push(position.clone())
        } else {
          _this.handler.destroy()
          if (typeof options.callback === 'function') {
            options.callback({
              e: trianglesEntity,
              e2: tempLineEntity,
              e3: tempLineEntity2
            })
          }
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
      // mouse
      this.handler.setInputAction(function (movement) {
        const position = _this.getCatesian3FromPX(movement.endPosition)
        if (position && positions.length > 0) {
          //直线
          positions.pop()
          positions.push(position.clone())
          let horizontalPosition = computesHorizontalLine(positions)
          //高度
          tempPoints.pop()
          tempPoints.push(horizontalPosition.clone())
          //水平线
          tempPoints2.pop()
          tempPoints2.pop()
          tempPoints2.push(position.clone())
          tempPoints2.push(horizontalPosition.clone())
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      // create entity
      //直线
      trianglesEntity.polyline = {
        positions: new Cesium.CallbackProperty(function () {
          return positions
        }, false),
        ...options.style
      }
      trianglesEntity.position = new Cesium.CallbackProperty(function () {
        return positions[0]
      }, false)
      trianglesEntity.point = {
        pixelSize: 5,
        outlineColor: Cesium.Color.RED,
        outlineWidth: 2
      }
      trianglesEntity.label = {
        text: new Cesium.CallbackProperty(function () {
          return '直线:' + (_this.getPositionDistance(_this.transformCartesianArrayToWGS84Array(positions)) * 1).toFixed(2) + '米'
        }, false),
        show: true,
        showBackground: true,
        font: '16px monospace',
        horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(50, -100) //left top
      }
      //高度
      tempLineEntity.polyline = {
        positions: new Cesium.CallbackProperty(function () {
          return tempPoints
        }, false),
        ...options.style
      }
      tempLineEntity.position = new Cesium.CallbackProperty(function () {
        return tempPoints2[1]
      }, false)
      tempLineEntity.point = {
        pixelSize: 5,
        outlineColor: Cesium.Color.RED,
        outlineWidth: 2
      }
      tempLineEntity.label = {
        text: new Cesium.CallbackProperty(function () {
          return '高度:' + getHeading(tempPoints[0], tempPoints[1]) + '米'
        }, false),
        show: true,
        showBackground: true,
        font: '16px monospace',
        horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(-20, 100) //left top
      }
      //水平
      tempLineEntity2.polyline = {
        positions: new Cesium.CallbackProperty(function () {
          return tempPoints2
        }, false),
        ...options.style
      }
      tempLineEntity2.position = new Cesium.CallbackProperty(function () {
        return positions[1]
      }, false)
      tempLineEntity2.point = {
        pixelSize: 5,
        outlineColor: Cesium.Color.RED,
        outlineWidth: 2
      }
      tempLineEntity2.label = {
        text: new Cesium.CallbackProperty(function () {
          return '水平距离:' + (_this.getPositionDistance(_this.transformCartesianArrayToWGS84Array(tempPoints2)) * 1).toFixed(2) + '米'
        }, false),
        show: true,
        showBackground: true,
        font: '16px monospace',
        horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(-150, -20) //left top
      }
      this.drawLayer.entities.add(tempLineEntity2)
      this.drawLayer.entities.add(tempLineEntity)
      this.drawLayer.entities.add(trianglesEntity)
    }
  };

  clearDrawMeasureGraphics() {
    this.removeDrawMeasureAction()
    this.drawLayer.entities.removeAll()
  }
}
